001 /*
002 * Copyright 2004 Stephen J. McConnell.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.metro.info;
020
021 import java.util.Properties;
022
023 import net.dpml.lang.Version;
024
025 /**
026 * This class is used to provide explicit information to assembler
027 * and administrator about the Component. It includes information
028 * such as;
029 *
030 * <ul>
031 * <li>a symbolic name</li>
032 * <li>classname</li>
033 * <li>version</li>
034 * </ul>
035 *
036 * <p>The InfoDescriptor also includes an arbitrary set
037 * of attributes about component. Usually these are container
038 * specific attributes that can store arbitrary information.
039 * The attributes should be stored with keys based on package
040 * name of container.
041 *
042 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
043 * @version 1.0.0
044 */
045 public final class InfoDescriptor extends Descriptor
046 {
047 //-------------------------------------------------------------------
048 // static
049 //-------------------------------------------------------------------
050
051 /**
052 * Serial version identifier.
053 */
054 static final long serialVersionUID = 1L;
055
056 //-------------------------------------------------------------------
057 // immutable state
058 //-------------------------------------------------------------------
059
060 /**
061 * The short name of the Component Type. Useful for displaying
062 * human readable strings describing the type in
063 * assembly tools or generators.
064 */
065 private final String m_name;
066
067 /**
068 * The implementation classname.
069 */
070 private final String m_classname;
071
072 /**
073 * The version of component that descriptor describes.
074 */
075 private final Version m_version;
076
077 /**
078 * The component lifestyle.
079 */
080 private final LifestylePolicy m_lifestyle;
081
082 /**
083 * The component garbage collection policy. The value returned is either
084 * WEAK, SOFT, HARD or HARD. A component implementing a WEAK policy
085 * will be decommissioned if no references exist. A component declaring a
086 * SOFT policy will exist without reference so long as memory contention
087 * does not occur. A component implementing HARD policies will be
088 * maintained irrespective of usage and memory constraints. The default
089 * policy is SYSTEM which implies delegation of policy selection to the
090 * component's container.
091 */
092 private final CollectionPolicy m_collection;
093
094 /**
095 * Threadsafe policy.
096 */
097 private final ThreadSafePolicy m_threadsafe;
098
099
100 //-------------------------------------------------------------------
101 // constructor
102 //-------------------------------------------------------------------
103
104 /**
105 * Creation of a new info descriptor using a supplied name, key, version
106 * and attribute set.
107 *
108 * @param name the default component name
109 * @param classname the implemetation classname
110 * @exception IllegalArgumentException if the classname is invalid
111 * @exception NullPointerException if the classname is null
112 */
113 public InfoDescriptor( final String name, final String classname )
114 throws IllegalArgumentException, NullPointerException
115 {
116 this( name, classname, null, null, CollectionPolicy.SYSTEM, ThreadSafePolicy.UNKNOWN, null );
117 }
118
119 /**
120 * Creation of a new info descriptor using a supplied name, key, version
121 * and attribute set.
122 *
123 * @param name the default component name
124 * @param classname the implemetation classname
125 * @param version the implementation version
126 * @param lifestyle the component lifestyle (singleton, thread, etc.)
127 * @param collection the garbage collection policy for the component
128 * @param threadsafe if TRUE the type is declaring itself as threadsafe
129 * @param attributes a set of attributes associated with the component type
130 * @exception IllegalArgumentException if the implementation classname is invalid
131 * @exception NullPointerException if the classname argument is null.
132 */
133 public InfoDescriptor( final String name,
134 final String classname,
135 final Version version,
136 final LifestylePolicy lifestyle,
137 final CollectionPolicy collection,
138 final ThreadSafePolicy threadsafe,
139 final Properties attributes )
140 throws IllegalArgumentException, NullPointerException
141 {
142 super( attributes );
143
144 m_threadsafe = threadsafe;
145
146 if( null == classname )
147 {
148 throw new NullPointerException( "classname" );
149 }
150
151 if( classname.indexOf( "/" ) > -1 )
152 {
153 throw new IllegalArgumentException( "classname: " + classname );
154 }
155
156 m_classname = classname;
157
158 if( null == version )
159 {
160 m_version = Version.parse( "0" );
161 }
162 else
163 {
164 m_version = version;
165 }
166
167 if( lifestyle == null )
168 {
169 m_lifestyle = LifestylePolicy.SYSTEM;
170 }
171 else
172 {
173 m_lifestyle = lifestyle;
174 }
175
176 if( null == collection )
177 {
178 m_collection = CollectionPolicy.SYSTEM;
179 }
180 else
181 {
182 m_collection = collection;
183 }
184
185 if( name != null )
186 {
187 m_name = name;
188 }
189 else
190 {
191 m_name = getClassName( classname );
192 }
193 }
194
195 /**
196 * Internal utility to get the name of the class without the package name. Used
197 * when constructing a default component name.
198 * @param classname the fully qualified classname
199 * @return the short class name without the package name
200 */
201 private String getClassName( String classname )
202 {
203 int i = classname.lastIndexOf( "." );
204 if( i == -1 )
205 {
206 return classname.toLowerCase();
207 }
208 else
209 {
210 return classname.substring( i + 1, classname.length() ).toLowerCase();
211 }
212 }
213
214 /**
215 * Return the symbolic name of component.
216 *
217 * @return the symbolic name of component.
218 */
219 public String getName()
220 {
221 return m_name;
222 }
223
224 /**
225 * Return the component collection policy.
226 *
227 * @return the policy
228 */
229 public CollectionPolicy getCollectionPolicy()
230 {
231 return m_collection;
232 }
233
234 /**
235 * Test is the component type implements a weak collection policy.
236 *
237 * @return TRUE if the policy is weak
238 */
239 public boolean isWeak()
240 {
241 return m_collection.equals( CollectionPolicy.WEAK );
242 }
243
244 /**
245 * Test is the component type implements a soft collection policy.
246 *
247 * @return TRUE if the policy is soft
248 */
249 public boolean isSoft()
250 {
251 return m_collection.equals( CollectionPolicy.SOFT );
252 }
253
254 /**
255 * Test is the component type implements a hard collection policy.
256 *
257 * @return TRUE if the policy is hard
258 */
259 public boolean isHard()
260 {
261 return m_collection.equals( CollectionPolicy.HARD );
262 }
263
264 /**
265 * Return the implementation class name for the component type.
266 *
267 * @return the implementation class name
268 */
269 public String getClassname()
270 {
271 return m_classname;
272 }
273
274 /**
275 * Return the version of component.
276 *
277 * @return the version of component.
278 */
279 public Version getVersion()
280 {
281 return m_version;
282 }
283
284 /**
285 * Return the component lifestyle policy.
286 *
287 * @return the lifestyle policy
288 */
289 public LifestylePolicy getLifestylePolicy()
290 {
291 return m_lifestyle;
292 }
293
294 /**
295 * Ruturn the thread-safe status.
296 *
297 * @return the thread-safe status
298 */
299 public boolean isThreadSafe()
300 {
301 return m_threadsafe.equals( ThreadSafePolicy.TRUE );
302 }
303
304 /**
305 * Ruturn the thread-safe policy value.
306 *
307 * @return the thread-safe policy value
308 */
309 public ThreadSafePolicy getThreadSafePolicy()
310 {
311 return m_threadsafe;
312 }
313
314 /**
315 * Return a string representation of the info descriptor.
316 * @return the stringified type
317 */
318 public String toString()
319 {
320 return "[" + getName() + "] " + getClassname() + ":" + getVersion();
321 }
322
323 /**
324 * Test is the supplied object is equal to this object.
325 * @param other the other object
326 * @return true if the object are equivalent
327 */
328 public boolean equals( Object other )
329 {
330 boolean isEqual = super.equals( other ) && other instanceof InfoDescriptor;
331 if( isEqual )
332 {
333 InfoDescriptor info = (InfoDescriptor) other;
334 isEqual = isEqual && m_threadsafe.equals( info.m_threadsafe );
335 isEqual = isEqual && m_classname.equals( info.m_classname );
336 isEqual = isEqual && m_collection.equals( info.m_collection );
337 isEqual = isEqual && m_name.equals( info.m_name );
338 isEqual = isEqual && m_lifestyle.equals( info.m_lifestyle );
339 if( null == m_version )
340 {
341 isEqual = isEqual && null == info.m_version;
342 }
343 else
344 {
345 isEqual = isEqual && m_version.equals( info.m_version );
346 }
347 }
348 return isEqual;
349 }
350
351 /**
352 * Return the hashcode for the object.
353 * @return the hashcode value
354 */
355 public int hashCode()
356 {
357 int hash = super.hashCode();
358 hash ^= m_collection.hashCode();
359 hash ^= m_threadsafe.hashCode();
360 hash ^= m_classname.hashCode();
361 if ( null != m_name )
362 {
363 hash ^= m_name.hashCode();
364 }
365
366 if ( null != m_lifestyle )
367 {
368 hash = hash + 9873234;
369 hash ^= m_lifestyle.hashCode();
370 }
371
372 if ( null != m_version )
373 {
374 hash ^= m_version.hashCode();
375 }
376 return hash;
377 }
378 }